#pragma once

#include <iostream>
#include <pthread.h>
#include <string>
#include <cstring>
#include <functional>
#include <cassert>

class Thread;

class Context // 上下文，将pthread_create中类内的第4个参数和this合并
{
public:
    Context()
        : _this(nullptr),
          _args(nullptr)
    {
    }
    ~Context()
    {
    }
    Thread *_this; // 调用函数的this指针（pthread_create创建线程之后，线程执行函数的参数的this）
    void *_args;   // 保存线程执行函数的参数
};

class Thread
{
public:
    // using func_t = std::function<void*(void*)>;作用同下
    typedef std::function<void *(void *)> func_t;
    const int num = 1024;
    //这样改就可以像C++一样，直接构造一个线程，然后传线程执行函数就行，不需要传线程函数参数和线程编号了！
    //Thread(func_t func, void *args = nullptr, int number = 0)
    Thread(func_t func, void *args, int number)
        : _func(func),
          _args(args)
    {
        // _name = "thread : ";
        // _name += std::to_string(number);//和下面snprintf作用相同
        char buffer[num];
        snprintf(buffer, sizeof buffer, "thread : %d", number);
        _name = buffer;

        //void start()//可以直接把start函数拿进来
        //{
        Context *cnt = new Context();
        cnt->_args = _args;
        cnt->_this = this;
        // 这里直接把cnt进行传参，直接把start_routine的参数和this指针都传过去了
        int n = pthread_create(&_tid, nullptr, start_routine, cnt); // 这里_func会报错，C接口不能直接掉C++的函数对象
        assert(0 == n);                                             // 线程创建成功函数返回值为0
        (void)n;                                                    // 编译器在release版本会注释掉assert。会发现我们没有使用n，有的编译器就会报存在未使用变量n的警告，我们这里取消这个警告
        //}
    }

    // 在类内创建线程，想让线程执行对应的方法，需要将方法设置为static(静态方法) —— 因为static类内函数没有this指针！

    static void *start_routine(void *args) // 写一个函数，方便我们下面pthread_create第3个参数使用
    {
        // 很不幸，下面还是不能直接使用start_routine函数，因为start_routine是类内函数，有缺少参数！
        // 也就是说start_routine有两个参数，第一个参数是Thread* this指针，第二个参数才是void* args
        // return _func(args);

        // 这里就又出问题了，静态函数只能调用静态方法和静态成员，不能调用类内成员方法和成员变量！
        // 所以得换一种写法

        Context *cnt = static_cast<Context *>(args);
        void *ret = cnt->_this->run(cnt->_args); // 这里调用下面的run函数
        delete cnt;
        return ret;
    }
    // void start()//这里把start放外面，调用的时候要让线程调用start
    // {
    //     Context *cnt = new Context();
    //     cnt->_args = _args;
    //     cnt->_this = this;
    //     // 这里直接把cnt进行传参，直接把start_routine的参数和this指针都传过去了
    //     int n = pthread_create(&_tid, nullptr, start_routine, cnt); // 这里_func会报错，C接口不能直接掉C++的函数对象
    //     assert(0 == n);                                             // 线程创建成功函数返回值为0
    //     (void)n;                                                    // 编译器在release版本会注释掉assert。会发现我们没有使用n，有的编译器就会报存在未使用变量n的警告，我们这里取消这个警告
    // }

    void join()
    {
        int n = pthread_join(_tid, nullptr);
        assert(n == 0);
        (void)n;
        //printf("%s\n", strerror(n));
    }

    void *run(void *args) // 给上面start_routine来用的
    {
        return _func(args);
    }

    ~Thread()
    {
    }

private:
    std::string _name; // 我们想直接看线程名字，比如线程1，线程2这种
    pthread_t _tid;
    func_t _func; // 线程未来执行的函数
    void *_args;  // 线程执行函数的参数
};
